import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.metrics import accuracy_score

def sigmod(x):
    res = 1.0 / (1 + np.exp(-1 * x))
    return res

def forward(x, w, b):
    param = np.dot(x,w) + b
    res = sigmod(param)
    return res

def error_show(data, ture_label, pred_label):
    range_num = int(np.shape(pred_label)[0])
    error_index = [i for i in range(range_num) if ture_label[i]!=pred_label[i]]
    res = data[error_index,:],pred_label[error_index,:]
    return res

def pred(data, label, yita = 0.01, n_max = 2000):
    # 输入维度
    input_dim = np.shape(data)[1]
    # 隐藏层维度
    hide_dim = 8
    # 输出层维度
    out_dim = 2
    # 权重初始化
    W1 = np.random.randn(input_dim,hide_dim) / np.sqrt(input_dim)
    b1 = np.zeros((1,hide_dim))
    W2 = np.random.randn(hide_dim,out_dim) / np.sqrt(hide_dim)
    b2 = np.zeros((1,out_dim))
    n = 0
    while n < n_max:
        a = forward(data,W1,b1)
        y = forward(a,W2,b2)
        error_out = y * (1-y) * (label-y)
        error_hide = a * (1-a) * np.dot(error_out,W2.T)
        W1=W1+yita*np.dot(data.T,error_hide)
        b1=b1+yita*np.sum(error_hide,axis=0)
        W2=W2+yita*np.dot(a.T,error_out)
        b2=b2+yita*np.sum(error_out,axis=0)
        n=n+1
    y_pred=np.argmax(y,axis=1).reshape(np.shape(data)[0],1)
    return y_pred


# 读取数据
np.random.seed(0)
X, y = datasets.make_moons(200, noise = 0.20)

# 函数方式实现网络正向计算和反向误差传播、权值更新
plt.figure(1)
plt.scatter(X[:, 0], X[:, 1], c = y, cmap = plt.cm.Spectral)
plt.title("turth")
plt.xlim(-1.6,2.5)
plt.ylim(-1.1,1.5)
t = np.zeros((np.shape(X)[0],2))
t[np.where(y==0),0] = 1
t[np.where(y==1),1] = 1
out_pred = pred(X,t)
acc = accuracy_score(y,out_pred)
print("准确率:%f" % acc)
plt.figure(2)
plt.scatter(X[:,0],X[:,1], c = out_pred,cmap = plt.cm.Spectral)
plt.title("predicted")
plt.xlim(-1.6,2.5)
plt.ylim(-1.1,1.5)

# 错误样本可视化
error_data,error_label = error_show(X,y,out_pred)
plt.figure(3)
plt.scatter(error_data[:,0],error_data[:,1],c = error_label,cmap = plt.cm.Spectral)
plt.title("predicted incorrect data")
plt.xlim(-1.6,2.5)
plt.ylim(-1.1,1.5)
plt.show()